// Learn TypeScript:
//  - https://docs.cocos.com/creator/manual/en/scripting/typescript.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html


// type SpriteType = cc.Sprite.Type;
// const SpriteType = cc.Sprite.Type;

// type SpriteSizeMode = cc.Sprite.SizeMode;
// const SpriteSizeMode = cc.Sprite.SizeMode;

// enum SpriteType {
//     SIMPLE = cc.Sprite.Type.SIMPLE,
//     TILED = cc.Sprite.Type.TILED,
// }

// enum SpriteSizeMode {
//     CUSTOM = cc.Sprite.SizeMode.CUSTOM,
//     RAW = cc.Sprite.SizeMode.RAW,
// }

enum SupportType {
    MP4 = "video/mp4",
    OGG = "video/ogg",
    WEBM = "video/webm",
}

export enum EventType {
    None,
    Playing,
    Paused,
    Stopped,
    Completed,
    MetaLoaded,
    // Clicked,
    ReadyToPlay,
}

enum ResType {
    Remote,
    Local,
}

enum ElemState {
    None = 0x00,
    Initial = 0x01,
    Ready = 0x02,
    LoadedMeta = 0x04,
    Loaded = 0x08,
}

enum ElemReadyState {
    HaveNothing = 0,
    HaveMetaData = 1,
    HaveCurrentData = 2,
    HaveFutureData = 3,
    HaveEnoughData = 4
};

enum ElemAction {
    Play,
    Pause,
}

const LogStyle = [
    "background-color:black",
    "color:white",
].join(';');

function log(info, ...params) {
    console.log(`%c[VideoGLPlayer]${info}`, LogStyle, ...params);
}

const {
    ccclass,
    requireComponent,
    property,
    executeInEditMode,
    disallowMultiple,
} = cc._decorator;

@ccclass
@executeInEditMode
@disallowMultiple
// @requireComponent(cc.Sprite)
export default class VideoGLPlayer extends cc.Component {
    // private static __assembler__ = cc.Sprite["__assembler__"];

    // protected _spriteFrame:cc.SpriteFrame = null; 
    // protected get()

    // @property({type:cc.Enum(SpriteType)})
    // private _type:SpriteType = SpriteType.SIMPLE;

    // @property({type:cc.Enum(SpriteType)})
    // get Type():SpriteType {
    //     return this._type;
    // }
    // set Type(value:SpriteType) {
    //     if (this._type !== value) {
    //         this._type = value;
    //         this["setVertsDirty"]();
    //         this["_resetAssembler"]();
    //     }
    // }

    static CanPlayType:Array<string> = [];

    protected _element:HTMLVideoElement = null;
    protected _ratioVideo:number = null;
    

    protected _bufCanvas:HTMLCanvasElement = null;
    protected _bufContext:CanvasRenderingContext2D = null;

    // protected _display:cc.Sprite = null;
    protected _videoSize:cc.Size = cc.Size.ZERO;
    protected _texture:cc.Texture2D = null;
    protected _texUpdateOpt = null;
    
    // first touch
    protected _touchDocHandler: (this: Window, ev: TouchEvent) => any = null;
    protected _playedWithoutTouch:boolean = false;

    protected _curState:EventType = EventType.None;
    protected _elemState:ElemState = ElemState.None;
    protected _elemAction:ElemAction = ElemAction.Pause;

    private _ignorePause:boolean = false;

    protected _listeners = {
        videoLoadedMeta : this.onVideoLoadedMeta.bind(this),
        videoEnded : this.onVideoEnded.bind(this),
        videoPlay : this.onVideoPlay.bind(this),
        videoPause : this.onVideoPause.bind(this),
        videoCanPlay : this.onVideoCanPlay.bind(this),
        videoError : this.onVideoError.bind(this),
    };

    get usedCanvas(): boolean {
        return !!this._bufCanvas;
    }
    set usedCanvas(val:boolean) {
        let uesed = this.usedCanvas;
        if(uesed === val)
            return;

        if(val) {
            this._bufCanvas = document.createElement("canvas");
            this._bufContext = this._bufCanvas.getContext("2d", { alpha: false });

            this.setupRenderer(this._element);

            // this._bufCanvas.id = "VideoGLBuffCanvas"
            // this._bufCanvas.style.zIndex = "999";
            // cc.game.container.appendChild(this._bufCanvas);
        }
        else {
            // if(this._bufCanvas)
            //     cc.game.container.removeChild(this._bufCanvas);

            this._bufCanvas = null;
            this._bufContext = null;
        }
    }

    @property(cc.Sprite)
    protected _renderer:cc.Sprite = null;
    @property(cc.Sprite)
    get renderer():cc.Sprite {
        return this._renderer;
    }
    set renderer(val:cc.Sprite) {
        if(val && val.node === this.node)
            return;

        this.setupRenderer(null);
        this._renderer = val;
        let elem = this._element;
        if(elem && this.checkElemState(ElemState.Loaded)){
            this.setupRenderer(elem);
            if(this._ratioVideo)
                this.fixedRendererSize(this._ratioVideo);
        }
    }

    @property
    private _resType:ResType = ResType.Remote;
    @property({type:cc.Enum(ResType)})
    get resType():ResType {
        return this._resType;
    }
    set resType(val:ResType) {
        if(this._resType === val)
            return;

        log(`set resType=${val}`);
        this._resType = val;
        this.setupRenderer(null);

        let url:string;
        switch(this._resType) {
            case ResType.Local : if(this._clip) url = this._clip.nativeUrl; break;
            case ResType.Remote: url = this._remoteURL; break;
        }
        this.setElementSrc(url);
    }

    @property
    private _remoteURL:string = "";
    @property({visible(){return this._resType === ResType.Remote;}})
    get remoteURL():string {
        return this._remoteURL;
    }
    set remoteURL(val:string) {
        if(this._remoteURL === val)
            return;

        log(`set remoteURL=${val}`);
        this._remoteURL = val;
        this.setupRenderer(null);
        this.setElementSrc(val);
    }

    @property
    private _clip:cc.Asset = null;
    @property({type:cc.Asset, visible(){return this._resType === ResType.Local;}})
    get clip():cc.Asset {
        return this._clip;
    }
    set clip(val:cc.Asset) {
        if(this._clip === val)
            return;

        log(`set remoteURL=${val}`);
        this._clip = val;
        this.setupRenderer(null);

        if(val)
            this.setElementSrc(val.nativeUrl);
    }

    @property(cc.Node)
    private _nodeAdapting:cc.Node = this.node;
    @property(cc.Node)
    get nodeAdapting():cc.Node {
        return this._nodeAdapting;
    }
    set nodeAdapting(val:cc.Node) {
        if(val && val === this._nodeAdapting)
            return;

        if(this._renderer && val && this._renderer.node === val)
            return;

        if(this._nodeAdapting)
            this._nodeAdapting.off(cc.Node.EventType.SIZE_CHANGED, this.onNodeAdaptingSizeChanged, this);

        this._nodeAdapting = val;

        if(val)
            val.on(cc.Node.EventType.SIZE_CHANGED, this.onNodeAdaptingSizeChanged, this);

        let elem = this._element;
        if(elem && this.checkElemState(ElemState.Loaded) && this._ratioVideo)
            this.fixedRendererSize(this._ratioVideo);
    }

    @property
    private _keepAspectRatio:boolean = true;
    @property
    get keepAspectRatio():boolean {
        return this._keepAspectRatio;
    }
    set keepAspectRatio(val:boolean) {
        this._keepAspectRatio = val;
        let elem = this._element;
        if(elem && this.checkElemState(ElemState.Loaded) && this._ratioVideo)
            this.fixedRendererSize(this._ratioVideo);
    }

    

    // @property({type:cc.Enum(SpriteType)})
    // private _renderType:SpriteType = SpriteType.SIMPLE;
    // @property({type:cc.Enum(SpriteType)})
    // get renderType():SpriteType {
    //     return this._renderType;
    // }
    // set renderType(val:SpriteType) {
    //     this._renderType = val;
    //     if(this._display)
    //         this._display.type = <any>val;
    // }

    // @property({type:cc.Enum(SpriteSizeMode)})
    // private _sizeMode:SpriteSizeMode = SpriteSizeMode.RAW;
    // @property({type:cc.Enum(SpriteSizeMode)})
    // get sizeMode():SpriteSizeMode {
    //     return this._sizeMode;
    // }
    // set sizeMode(val:SpriteSizeMode) {
    //     this._sizeMode = val;
    //     if(this._display)
    //         this._display.sizeMode = <any>val;
    // }



    @property
    private _volume:number = 1;
    @property({type:cc.Float, range: [0, 1], slide:true})
    get volume():number {
        return this._volume;
    }
    set volume(val:number) {
        log(`set volume=${val}`);
        val = Math.min(1, Math.max(val, 0));
        this._volume = val;
        if(this._element)
            this._element.volume = val;
    }

    @property
    private _autoPlay:boolean = false;
    @property
    get autoPlay():boolean {
        return this._autoPlay;
    }
    set autoPlay(val:boolean) {
        log(`set autoPlay=${val}`);
        this._autoPlay = val;
        if(!CC_EDITOR && this._element)
            this._element.autoplay = val;
    }

    @property
    private _mute:boolean = false;
    @property
    get mute():boolean {
        return this._mute;
    }
    set mute(val:boolean) {
        log(`set mute=${val}`);
        this._mute = val;
        if(this._element)
            this._element.muted = val;
    }

    @property
    private _loop:boolean = false;
    @property
    get loop():boolean {
        return this._loop;
    }
    set loop(val:boolean) {
        log(`set loop=${val}`);
        this._loop = val;
        if(this._element)
            this._element.loop = val;
    }

    get duration() {
        if(!this._element || !this.checkElemState(ElemState.LoadedMeta) )
            return -1;
        
        return this._element.duration;
    }

    get currentTime() {
        if(this._element ) {
            if(this._curState === EventType.None ||
                this._curState === EventType.Stopped ||
                this._curState === EventType.MetaLoaded ||
                this._curState === EventType.ReadyToPlay) {
                return 0;
            }
            else if(this._curState === EventType.Completed) {
                return this._element.duration;
            }

            return this._element.currentTime;
        }

        return -1;
    }
    set currentTime(val:number) {
        log(`set currentTime=${val}`);
        if(this._element)
            this._element.currentTime = val;
    }

    @property([cc.Component.EventHandler])
    videoPlayerEvent:cc.Component.EventHandler[] = [];

    onLoad () {
        // this.node.addChild(new cc.Spri);
        // this._display = this.getComponent(cc.Sprite);
        // if(!this._display) this._display = this.addComponent(cc.Sprite);
        // this._display.trim = false;
        // this._display.type = <any>this._renderType;
        // this._display.sizeMode = <any>this._sizeMode;
        // this._display.spriteFrame = new cc.SpriteFrame();
        log(`onLoad.`);

        let elem = document.createElement("video");

        let canPlayType = VideoGLPlayer.CanPlayType;
        if(canPlayType.length <= 0) {
            if (elem.canPlayType(SupportType.OGG)) {
                canPlayType.push(".ogg");
                canPlayType.push(".ogv");
            }
            if (elem.canPlayType(SupportType.MP4)) {
                canPlayType.push(".mp4");
            }
            if (elem.canPlayType(SupportType.WEBM)) {
                canPlayType.push(".webm");
            }
        }

        this._element = elem;
        elem.autoplay = this._autoPlay;
        elem.loop = this._loop;
        elem.muted = this._mute;
        elem.volume = this._volume;
        elem.preload = "auto";
        elem.crossOrigin = "*";

        elem.style.position = "absolute";
        elem.style.bottom = "0px";
        elem.style.left = "0px";
        elem.style.visibility = "visible";
        
        // try to disable the iPhone video fullscreen mode:
        elem.setAttribute("playsinline", "");
        elem.setAttribute("webkit-playsinline", "");

        // elem.setAttribute("x-webkit-airplay", "true");
        elem.setAttribute("x5-playsinline", "");
        elem.setAttribute("x5-video-player-type","h5");
        elem.setAttribute("x5-video-orientation","landscape");
        elem.setAttribute("x5-video-player-fullscreen", "true");

        elem.addEventListener('loadedmetadata', this._listeners.videoLoadedMeta);
    
        elem.addEventListener('play', this._listeners.videoPlay);
        elem.addEventListener('pause', this._listeners.videoPause);
        elem.addEventListener('ended', this._listeners.videoEnded);

        elem.addEventListener('canplay', this._listeners.videoCanPlay);
        elem.addEventListener('canplaythrough', this._listeners.videoCanPlay);
        elem.addEventListener('suspend', this._listeners.videoCanPlay);

        this._texUpdateOpt = {
            // image: elem,
            genMipmaps: false,
            format: cc.Texture2D.PixelFormat.RGB888,
            minFilter: cc.Texture2D.Filter.LINEAR,
            magFilter: cc.Texture2D.Filter.LINEAR,
            wrapS: <any>cc.Texture2D.WrapMode.CLAMP_TO_EDGE,
            wrapT: <any>cc.Texture2D.WrapMode.CLAMP_TO_EDGE,
            premultiplyAlpha: false,
            flipY: false,
        };

        if(CC_EDITOR) {
            // this._element.autoplay = false;
            // this.node.on(cc.Node.EventType.SIZE_CHANGED, this.resizedInEditor, this);
            if(this._nodeAdapting)
                this._nodeAdapting.on(cc.Node.EventType.SIZE_CHANGED, this.onNodeAdaptingSizeChanged, this);
        }
        this.node.on(cc.Node.EventType.SIZE_CHANGED, this.onSizeChanged, this);

        this._elemState |= ElemState.Initial;

        let url:string;
        switch(this._resType) {
            case ResType.Local : if(this._clip) url = this._clip.nativeUrl; break;
            case ResType.Remote: url = this._remoteURL; break;
        }
        this.setupRenderer(null);
        this.setElementSrc(url);
    }

    onDestroy() {
        log(`onDestroy.`);

        this.node.off(cc.Node.EventType.SIZE_CHANGED, this.onSizeChanged, this);
        if(CC_EDITOR) {
            // this.node.off(cc.Node.EventType.SIZE_CHANGED, this.resizedInEditor, this);
            if(this._nodeAdapting) {
                this._nodeAdapting.off(cc.Node.EventType.SIZE_CHANGED, this.onNodeAdaptingSizeChanged, this);
            }
        }

        this.nodeAdapting = null;

        // if(this.checkElemState(ElemState.None))
        //     return;

        let elem = this._element;
        if(elem) {
            elem.removeEventListener('loadedmetadata', this._listeners.videoLoadedMeta);
        
            elem.removeEventListener('play', this._listeners.videoPlay);
            elem.removeEventListener('pause', this._listeners.videoPause);
            elem.removeEventListener('ended', this._listeners.videoEnded);

            elem.removeEventListener('canplay', this._listeners.videoCanPlay);
            elem.removeEventListener('canplaythrough', this._listeners.videoCanPlay);
            elem.removeEventListener('suspend', this._listeners.videoCanPlay);
            this._element = null;
        }

        this._elemState = ElemState.None;
        this._elemAction = ElemAction.Pause;
    }

    onEnable() {
        this.enableElement();
    }

    onDisable() {
        this.disableElement();
    }

    private onSizeChanged() {
        // let elem = this._element;
        // if(elem && this.checkElemState(ElemState.Loaded)) {
        //     this.fixedAspectRatio(elem.videoWidth, elem.videoHeight);
        // }
    }

    private onNodeAdaptingSizeChanged() {
        //return;
        if(this._ratioVideo)
            this.fixedRendererSize(this._ratioVideo);
    }

    // start () {

    // }

    update (dt) {
        // if(this._texture) {
        //     this._texture.update({
        //         image: this._element,
        //         genMipmaps: false,
        //         format: cc.Texture2D.PixelFormat.RGB888,
        //         minFilter: cc.Texture2D.Filter.LINEAR,
        //         magFilter: cc.Texture2D.Filter.LINEAR,
        //         wrapS: <any>cc.Texture2D.WrapMode.CLAMP_TO_EDGE,
        //         wrapT: <any>cc.Texture2D.WrapMode.CLAMP_TO_EDGE,
        //         premultiplyAlpha: false,
        //     });
        // }

        let elem = this._element;
        if(!elem)
            return;

        log(`Video width=${elem.videoWidth}, height=${elem.videoHeight}`);

        // iOS - start video via touch
        if (this._curState === EventType.Playing && elem.paused && !this._touchDocHandler) {
            this._touchDocHandler = this.onTouchDocument.bind(this);
            log("Note - need a touch to start the video!");
            window.addEventListener("click", this._touchDocHandler, true);
        }

        if(this.checkElemState(ElemState.Loaded) 
            && this.checkElemAction(ElemAction.Play)
            && !elem.paused ) {
            
            if(this._videoSize.width !== elem.videoWidth || this._videoSize.height !== elem.videoHeight) {
                this.setupRenderer(elem);
            }

            if(this._texture) {
                let updateOpt = this._texUpdateOpt;
                if(this._bufCanvas) {
                    this.drawCanvas(elem);
                    updateOpt.image = this._bufCanvas;
                }
                else {
                    updateOpt.image = elem;
                }
                delete updateOpt.images;

                // let updateOpt = {
                //     image: elem,
                //     genMipmaps: false,
                //     format: cc.Texture2D.PixelFormat.RGB888,
                //     minFilter: cc.Texture2D.Filter.LINEAR,
                //     magFilter: cc.Texture2D.Filter.LINEAR,
                //     wrapS: <any>cc.Texture2D.WrapMode.CLAMP_TO_EDGE,
                //     wrapT: <any>cc.Texture2D.WrapMode.CLAMP_TO_EDGE,
                //     premultiplyAlpha: false,
                //     flipY: false,
                // };

                this._texture.update(<any>updateOpt);

                // this._texture.initWithElement(offScrCanvas);

                // let data = offScrCanvas.toDataURL('image/jpeg', 1);
                // let texture = new cc.Texture2D();
                // // texture.initWithElement(offScrCanvas);
                // texture.initWithData(<any>data, cc.Texture2D.PixelFormat.RGB888, elem.videoWidth, elem.videoHeight);
                // this._renderer.spriteFrame = new cc.SpriteFrame(texture);
                // this._texture = texture;
            }
        }
    }

    protected checkElemState(s:ElemState):boolean {
        if(s === ElemState.None) {
            return s === this._elemState;
        }
        else {
            let rlt = s & this._elemState;
            return rlt === s;
        }
    }

    protected checkElemAction(s:ElemAction):boolean {
        return this._elemAction === s;
    }

    protected enableElement() {
        // 激活video
        this._element && (this._element.currentTime = this._element.currentTime);
    }

    protected disableElement() {
        this._element && this._element.pause();
        this._elemAction = ElemAction.Pause;
    }

    protected setElementSrc(val:string) {
        if(!val || val === "")
            return;
        let extReg = /\.(\w+)\??/g;
        extReg.lastIndex = val.lastIndexOf(".");
        let result = extReg.exec(val);
        if (result && result.length > 1) {
            // let extname = result[1].toLowerCase();
            // let type:SupportType = SupportType.MP4;
            // switch(extname) {
            //     case "ogg":
            //     case "ogv" : type = SupportType.OGG;  break;
            //     case "mp4": type = SupportType.MP4;   break;
            //     case "webm": type = SupportType.WEBM;   break;
            //     default: return;
            // }

            let extname = result[0].toLowerCase();
            if(-1 == VideoGLPlayer.CanPlayType.indexOf(extname))
                return;

            this._ratioVideo = null;
            this._curState = EventType.None;
            this._elemState = ElemState.Initial;
            this._elemAction = ElemAction.Pause;

            let elem = this._element;
            if(elem) {
                elem.pause();
                // elem.src = val;
                var child = elem.lastElementChild;  
                while (child) { 
                    elem.removeChild(child); 
                    child = elem.lastElementChild; 
                } 

                // let extname = cc.path.extname(val);
                let canPlayType = VideoGLPlayer.CanPlayType;
                for (let i = 0; i < canPlayType.length; i++) {
                    let source = document.createElement("source");
                    source.src = val.replace(extname, canPlayType[i]);
                    elem.appendChild(source);
                }

                // (<any>this._element).src({type:type, src:val});
                elem.load();
            }
        }
    }

    // protected fixedAspectRatio(videoW:number, videoH:number) {
    //     if(!this._keepAspectRatio)
    //         return;

    //     if(!videoW || !videoH)
    //         return;

    //     // if(this._renderType !== SpriteType.SIMPLE)
    //     //     return;
            
    //     // if(this._sizeMode !== SpriteSizeMode.CUSTOM)
    //     //     return;

    //     let contentSize = this.node.getContentSize();
    //     let expectedW = contentSize.width;
    //     let expectedH = contentSize.height;
    //     if(!expectedH || !expectedW)
    //         return;

    //     let ratio = videoW / videoH;

    //     if(expectedW > expectedH) {
    //         if(videoW > videoH) expectedH = expectedW / ratio;
    //         else expectedW = expectedH * ratio;
    //     }
    //     else {
    //         if(videoW > videoH) expectedW = expectedH * ratio;
    //         else expectedH = expectedW / ratio;
    //     }

    //     this.node.setContentSize(expectedW, expectedH);
    // }

    protected fixedRendererSize(ratioVideo:number) {
        if(!this._renderer)
            return;

        let nodeRenderer = this._renderer.node;
        if(!nodeRenderer) 
            return;

        let nodeSize = nodeRenderer.getContentSize();
        let expectedW = nodeSize.width;
        let expectedH = nodeSize.height;

        let nodeAdapting = this.nodeAdapting;
        if(nodeAdapting) {
            let sizeAdapting = nodeAdapting.getContentSize();
            if(!sizeAdapting.width || !sizeAdapting.height)
                return;
            if(this.keepAspectRatio) {
            // if(false) {
                let ratioAdapting = sizeAdapting.width / sizeAdapting.height;
                if(ratioVideo > ratioAdapting) {
                    expectedW = sizeAdapting.width;
                    expectedH = expectedW / ratioVideo;
                }
                else {
                    expectedH = sizeAdapting.height;
                    expectedW = expectedH * ratioVideo;
                }
            }
            else {
                expectedW = sizeAdapting.width;
                expectedH = sizeAdapting.height;
            }

             let offAdapting = this.nodeAdapting.getAnchorPoint().sub(new cc.Vec2(0.5, 0.5));
            offAdapting.x *= sizeAdapting.width;
            offAdapting.y *= sizeAdapting.height;
            // cc.log(offAdapting);

            let offRenderer = new cc.Vec2(0.5, 0.5);
            nodeRenderer.getAnchorPoint().sub(offRenderer, offRenderer);
            offRenderer.x *= expectedW;
            offRenderer.y *= expectedH;

            nodeRenderer.setPosition(offRenderer.x - offAdapting.x, offRenderer.y - offAdapting.y);
        }
        else {
            let sizeAdapting = nodeRenderer.getContentSize();
            if(!sizeAdapting.width || !sizeAdapting.height)
                return;
            if(this.keepAspectRatio) {
                if(sizeAdapting.width > sizeAdapting.height) {
                    expectedW = sizeAdapting.width;
                    expectedH = expectedW / ratioVideo;
                }
                else {
                    expectedH = sizeAdapting.height;
                    expectedW = expectedH * ratioVideo;
                }
            }
        }
        // cc.log(expectedW, expectedH);

        nodeRenderer.setContentSize(expectedW, expectedH);
    }

    play() {
        if(!this.enabled || this.checkElemAction(ElemAction.Play))
            return;

        // need the first touch
        // if(this._touchHandler)
        //     return;
        
        
        log(`play.`);
        
        this._element && this._element.play();
    }

    stop() {
        if (!this.enabled)
            return;

        log(`stop.`);

        this._ignorePause = true;

        if(this._element) {
            this._element.currentTime = 0;
            this._element.pause();
        }

        const timeID = window.setTimeout(function () {
            window.clearTimeout(timeID);
            this._curState = EventType.Stopped;
            this.dispatchEvent(EventType.Stopped);
            this._ignorePause = false;
        }.bind(this), 0);
    }

    pause() {
        if (this.checkElemAction(ElemAction.Pause))
            return;

        log(`pause`);

        this._element && this._element.pause();
    }

    resume() {
        if(!this.enabled || this.checkElemAction(ElemAction.Play))
            return;

        log(`resume.`);

        this._element && this._element.play();
    }

    protected dispatchEvent(t:EventType, ...params) {
        cc.Component.EventHandler.emitEvents(this.videoPlayerEvent, this, t);
        this.node.emit(EventType[t]);
    }

    // protected clearTexture() {
    //     // let sf = this._display.spriteFrame;
    //     // if(sf) {
    //     //     sf.setTexture(null);
    //     //     this._texture = null;
    //     // }
    //     this._display.spriteFrame = null;
    //     this._texture = null;
    // }

    protected drawCanvas(elem:HTMLVideoElement) {
        // if(!this._bufContext)
        //     return;
        // if(!width && bufCanvas.width !== width) {
        //     bufCanvas.width = width;
        //     console.warn(`Evix, width=${bufCanvas.width}, height=${bufCanvas.height}`);
        // }

        // if(!heigth && bufCanvas.height !== heigth) {
        //     bufCanvas.height = heigth;
        //     console.warn(`Evix, width=${bufCanvas.width}, height=${bufCanvas.height}`);
        // }

        // ctx.clearRect(0, 0, width, heigth);
        // ctx.drawImage(elem, 0, 0, width, heigth);

        // this._bufContext.clearRect(0, 0, elem.videoWidth, elem.videoHeight);
        this._bufContext.drawImage(elem, 0, 0, elem.videoWidth, elem.videoHeight);
    }

    protected setupRenderer(elem:HTMLVideoElement) {
        let renderer = this._renderer;
        if(!renderer)
            return;

        if(elem) {
            let videoW = elem.videoWidth;
            let videoH = elem.videoHeight;
            if(videoW === 0 || videoH === 0)
                return;

            this._videoSize.width = videoW;
            this._videoSize.height = videoH;

            let bufCanvas = this._bufCanvas;
            if(this._texture){
                if(bufCanvas)
                    this._texture.initWithElement(bufCanvas);
                else
                    this._texture.initWithData(<any>elem, cc.Texture2D.PixelFormat.RGB888, videoW, videoH);
            }
            else {
                let texture = new cc.Texture2D();
                if(bufCanvas) 
                    texture.initWithElement(bufCanvas);
                else
                    texture.initWithData(<any>elem, cc.Texture2D.PixelFormat.RGB888, videoW, videoH);
                renderer.spriteFrame = new cc.SpriteFrame(texture);
                this._texture = texture;
            }

            if(bufCanvas) {
                bufCanvas.style.width= `${videoW}px`;
                bufCanvas.style.height = `${videoH}px`;

                this.drawCanvas(elem);
            }
        }
        else {
            this._videoSize.width = 0;
            this._videoSize.height = 0;

            renderer.spriteFrame = null;
            this._texture = null;
        }
    }

    private onTouchDocument(thiz: Window, ev: TouchEvent) {
        if(!this._touchDocHandler)
            return;

        log("onTouchDocument.");

        this._element.play();

        window.removeEventListener("click", this._touchDocHandler, true);
        this._touchDocHandler = null;
    }

    // protected onVideoReady() {
    //     log(`onVideoReady.`);

    //     this._elemState |= ElemState.Ready;
    //     // let player = this._player;
    // }

    protected onVideoLoadedMeta(evt:Event) {
        log(`onVideoLoadedMeta.`);

        this._elemState |= ElemState.LoadedMeta;

        cc.Component.EventHandler.emitEvents(this.videoPlayerEvent, this, EventType.MetaLoaded);
        this.node.emit(EventType[EventType.MetaLoaded]);
    }

    protected onVideoCanPlay(evt:Event) {
        if (this.checkElemState(ElemState.Loaded)
            || this.checkElemAction(ElemAction.Play) 
        )
            return;

        log(`onVideoCanPlay.`);

        let elem = this._element;
        if (elem.readyState === ElemReadyState.HaveEnoughData
            || elem.readyState === ElemReadyState.HaveMetaData) {

            // player.currentTime(0);
            // this._loaded = true;

            this._elemState |= ElemState.Loaded;

            // let texture:cc.Texture2D = null;
            // if(this._display.spriteFrame){
            //    texture = this._display.spriteFrame.getTexture();
            //    texture.initWithData(<any>elem, cc.Texture2D.PixelFormat.RGB888, elem.videoWidth, elem.videoHeight);
            // }
            // else {
            //     let texture = new cc.Texture2D();
            //     texture.initWithData(<any>elem, cc.Texture2D.PixelFormat.RGB888, elem.videoWidth, elem.videoHeight);
            //     this._texture = texture;
            //     this._display.spriteFrame = new cc.SpriteFrame(texture);
            // }

            this.setupRenderer(elem);

            if(elem.videoWidth != 0 && elem.videoHeight != 0) {
                let ratio = elem.videoWidth / elem.videoHeight;
                this._ratioVideo = ratio;
                this.fixedRendererSize(ratio);
            }

            this._curState = EventType.ReadyToPlay;
            this.dispatchEvent(EventType.ReadyToPlay);

            if (CC_EDITOR) {
                this.node.emit('spriteframe-changed', this);
                elem.play();
            }

            if(this.enabled)
                this.enableElement();
            else
                this.disableElement();
        }
    }

    protected onVideoEnded(evt:Event) {
        log(`onVideoEnded.`);

        this._elemAction = ElemAction.Pause;

        this._curState = EventType.Completed;
        this.dispatchEvent(EventType.Completed);
    }

    protected onVideoPlay(evt:Event) {
        log(`onVideoPlay.`);

        this._elemAction = ElemAction.Play;

        this._curState = EventType.Playing;
        this.dispatchEvent(EventType.Playing);

        if(this.enabled)
            this.enableElement();
        else
            this.disableElement();
    }

    protected onVideoPause(evt:Event) {
        log(`onVideoPause.`);

        this._elemAction = ElemAction.Pause;

        if(!this._ignorePause) {
            this._curState = EventType.Paused;
            this.dispatchEvent(EventType.Paused);
        }
    }

    protected onVideoError(evt:Event) {
        let elem = this._element;

        let err = "unknown error";
        switch (elem.error.code) {
            case 1: err = "video loading aborted"; break;
            case 2: err = "network loading error"; break;
            case 3: err = "video decoding failed / corrupted data or unsupported codec"; break;
            case 4: err = "video not supported"; break;
        };

        log("onVideoError: " + err + " (errorcode=" + elem.error.code + ")", "color:red;");
    }

    // @property({editorOnly:true})
    // private resizedInEditor() {
    //     let sf = this._display.spriteFrame;
    //     if (sf) {
    //         let actualSize = this.node.getContentSize();
    //         let expectedW = actualSize.width;
    //         let expectedH = actualSize.height;
    //         if (this._sizeMode === SpriteSizeMode.RAW) {
    //             let size = sf.getOriginalSize();
    //             expectedW = size.width;
    //             expectedH = size.height;
    //         }
    //         // else if (this._sizeMode === SpriteSizeMode.TRIMMED) {
    //         //     let rect = sf.getRect();
    //         //     expectedW = rect.width;
    //         //     expectedH = rect.height;
    //         // }

    //         if (expectedW !== actualSize.width || expectedH !== actualSize.height) {
    //             this._sizeMode = SpriteSizeMode.CUSTOM;
    //         }
    //     }
    //     else {
    //         this._sizeMode = SpriteSizeMode.CUSTOM;
    //     }
    // };
}
